package com.pyf.ui.refresh;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DragInfo;
import ohos.app.Context;

/**
 * 下拉刷新组件
 *
 * @author 裴云飞
 * @date 2020/12/27
 */
public class SwipeRefreshLayout extends ComponentContainer implements
        Component.DraggedListener, Component.LayoutRefreshedListener, IRefresh {

    /**
     * 下拉刷新的头部组件的状态
     */
    private HeadRefreshComponent.RefreshState mState;
    /**
     * 下拉刷新的头部组件
     */
    private HeadRefreshComponent mHeadRefreshComponent;
    private IRefresh.RefreshListener mRefreshListener;
    /**
     * 下拉刷新的时候是否禁止滚动
     */
    private boolean mDisableRefreshScroll;
    private int mLastY;
    private int mAutoLastY;
    private double mDownYOffset;
    private AnimatorValue value;

    public SwipeRefreshLayout(Context context) {
        this(context, null);
    }

    public SwipeRefreshLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public SwipeRefreshLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        setLayoutRefreshedListener(this);
        setDraggedListener(DRAG_VERTICAL, this);
        value = new AnimatorValue();
    }

    @Override
    public void onRefreshed(Component component) {
        int left = component.getLeft();
        int top = component.getTop();
        int right = component.getRight();
        int bottom = component.getBottom();
        Component head = getComponentAt(0);
        Component child = getComponentAt(1);
        if (head != null && child != null) {
            int childTop = child.getTop();
            if (mState == HeadRefreshComponent.RefreshState.STATE_REFRESH) {
                head.setComponentPosition(0, mHeadRefreshComponent.mPullRefreshHeight - head.getHeight(), right, mHeadRefreshComponent.mPullRefreshHeight);
                child.setComponentPosition(0, mHeadRefreshComponent.mPullRefreshHeight, right, mHeadRefreshComponent.mPullRefreshHeight + child.getHeight());
            } else {
                head.setComponentPosition(0, childTop - head.getHeight(), right, childTop);
                child.setComponentPosition(0, childTop, right, childTop + child.getHeight());
            }

            Component other;
            // 剩下的组件能够不跟随手势移动以实现一些特殊效果，如悬浮的效果
            for (int i = 2; i < getChildCount(); ++i) {
                other = getComponentAt(i);
                other.setComponentPosition(0, top, right, bottom);
            }
        }
    }

    @Override
    public void onDragDown(Component component, DragInfo dragInfo) {
        mDownYOffset = dragInfo.yOffset;
    }

    @Override
    public void onDragStart(Component component, DragInfo dragInfo) {
    }

    @Override
    public void onDragUpdate(Component component, DragInfo dragInfo) {
        double xOffset = dragInfo.xOffset;
        double yOffset = dragInfo.yOffset;
        if (Math.abs(xOffset) > Math.abs(yOffset)) {
            // 发生横向滚动
            return;
        }
        if (mRefreshListener != null && !mRefreshListener.enableRefresh()) {
            // 不允许下拉刷新
            return;
        }
        if (mDisableRefreshScroll && mState == HeadRefreshComponent.RefreshState.STATE_REFRESH) {
            // 刷新时禁止列表页滚动
            return;
        }
        Component head = getComponentAt(0);
        double distanceY = yOffset - mDownYOffset;
        // 没有刷新或者没有达到可刷新的距离，并且头部已经划出或者下拉
        if ((mState != HeadRefreshComponent.RefreshState.STATE_REFRESH || head.getBottom() <= mHeadRefreshComponent.mPullRefreshHeight) &&
                (head.getBottom() != 0 || distanceY >= 0)) {
            // 还在滑动中
            if (mState != HeadRefreshComponent.RefreshState.STATE_OVER_RELEASE) {
                int offsetY;
                // 根据阻尼计算需要滑动的距离
                if (head.getTop() < mHeadRefreshComponent.mPullRefreshHeight) {
                    // 没到可刷新的距离，减少阻尼
                    offsetY = (int) (mLastY / mHeadRefreshComponent.minDamp);
                } else {
                    // 达到可刷新的距离，加大阻尼
                    offsetY = (int) (mLastY / mHeadRefreshComponent.maxDamp);
                }
                // 如果是正在刷新状态，则不允许在滑动的时候改变状态
                // 用户手动下拉触发滑动，moveDown方法的第二个参数传false
                boolean bool = moveDown(offsetY, false);
                mLastY = (int) distanceY;
            }
        }
    }

    @Override
    public void onDragEnd(Component component, DragInfo dragInfo) {
        dragEndOrCancel();
    }

    @Override
    public void onDragCancel(Component component, DragInfo dragInfo) {
        dragEndOrCancel();
    }

    @Override
    public void setDisableRefreshScroll(boolean disableRefreshScroll) {
        mDisableRefreshScroll = disableRefreshScroll;
    }

    @Override
    public void refreshFinish() {
        Component head = getComponentAt(0);
        mState = HeadRefreshComponent.RefreshState.STATE_INIT;
        mHeadRefreshComponent.onFinish();
        mHeadRefreshComponent.setState(HeadRefreshComponent.RefreshState.STATE_INIT);
        int bottom = head.getBottom();
        if (bottom > 0) {
            // 刷新完成隐藏头部视图
            recover(bottom);
        }
    }

    @Override
    public void setRefreshListener(RefreshListener listener) {
        mRefreshListener = listener;
    }

    @Override
    public void setRefreshOverComponent(HeadRefreshComponent headRefreshComponent) {
        if (mHeadRefreshComponent != null) {
            removeComponent(mHeadRefreshComponent);
        }
        mHeadRefreshComponent = headRefreshComponent;
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
        addComponent(mHeadRefreshComponent, 0, config);
    }

    private void dragEndOrCancel() {
        Component head = getComponentAt(0);
        if (head.getBottom() > 0) {
            // 头部视图被拉下来了
            if (mState != HeadRefreshComponent.RefreshState.STATE_REFRESH) {
                // 松开手的时候不是刷新状态
                recover(head.getBottom());
                return;
            }
            mLastY = 0;
        }
    }

    private void recover(int dis) {
        if (mRefreshListener != null && dis > mHeadRefreshComponent.mPullRefreshHeight) {
            /*
             * 由于下拉的距离超过了下拉刷新的最小距离，所以需先要向上滚动一段距离，
             * 也就是需要滚动到mHeadRefreshComponent.mPullRefreshHeight这个距离，然后才开始刷新，
             * 需要向上滚动多少距离才能到达mHeadRefreshComponent.mPullRefreshHeight这个距离呢？
             * dis - mHeadRefreshComponent.mPullRefreshHeight就是需要向上滚动的距离
             */
            startScroll(dis - mHeadRefreshComponent.mPullRefreshHeight);
            mState = HeadRefreshComponent.RefreshState.STATE_OVER_RELEASE;
        } else {
            // 没有达到下拉刷新的最小距离，此时就应当直接向上滚动，也就是隐藏头部视图
            startScroll(dis);
        }
    }

    private void startScroll(int dis) {
        mAutoLastY = 0;
        value.setDuration(300);
        value.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        value.setValueUpdateListener((animatorValue, v) -> {
            float currY = dis * v;
            moveDown((int) (mAutoLastY - currY), true);
            mAutoLastY = (int) currY;
        });
        value.start();
    }

    /**
     * 根据偏移量滚动头部组件和子组件
     *
     * @param offsetY 偏移量
     * @param auto false手动触发滚动，true自动触发滚动
     * @return
     */
    private boolean moveDown(int offsetY, boolean auto) {
        Component head = getComponentAt(0);
        Component child = getComponentAt(1);
        int childTop = child.getTop() + offsetY;
        if (childTop <= 0) {
            // 异常情况处理
            offsetY = -child.getTop();
            // 移动到原始位置
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
            if (mState != HeadRefreshComponent.RefreshState.STATE_REFRESH) {
                mState = HeadRefreshComponent.RefreshState.STATE_INIT;
            }
        } else if (mState == HeadRefreshComponent.RefreshState.STATE_REFRESH && childTop > mHeadRefreshComponent.mPullRefreshHeight) {
            // 正在刷新中，禁止继续下拉
            return false;
        } else if (childTop <= mHeadRefreshComponent.mPullRefreshHeight) {
            // 没有超出可刷新的距离
            if (mHeadRefreshComponent.getState() != HeadRefreshComponent.RefreshState.STATE_REFRESH && !auto) {
                // 头部开始显示
                mHeadRefreshComponent.onVisible();
                mHeadRefreshComponent.setState(HeadRefreshComponent.RefreshState.STATE_VISIBLE);
                mState = HeadRefreshComponent.RefreshState.STATE_VISIBLE;
            }
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
            if (childTop == mHeadRefreshComponent.mPullRefreshHeight && mState == HeadRefreshComponent.RefreshState.STATE_OVER_RELEASE) {
                // 开始刷新
                refresh();
            }
        } else {
            if (mHeadRefreshComponent.getState() != HeadRefreshComponent.RefreshState.STATE_OVER && !auto) {
                // 超出刷新位置
                mHeadRefreshComponent.onOver();
                mHeadRefreshComponent.setState(HeadRefreshComponent.RefreshState.STATE_OVER);
            }
            offsetTopAndBottom(head, offsetY);
            offsetTopAndBottom(child, offsetY);
        }
        if (mHeadRefreshComponent != null) {
            mHeadRefreshComponent.onScroll(head.getBottom(), mHeadRefreshComponent.mPullRefreshHeight);
        }
        return true;
    }

    /**
     * 修改组件的位置来达到滚动的效果，只修改组件的垂直位置
     *
     * @param component
     * @param offset
     */
    private void offsetTopAndBottom(Component component, int offset) {
        component.setTop(component.getTop() + offset);
        component.setBottom(component.getBottom() + offset);
        component.setComponentPosition(component.getLeft(), component.getTop(), component.getRight(), component.getBottom());
    }

    /**
     * 开始刷新
     */
    private void refresh() {
        if (mRefreshListener != null) {
            mHeadRefreshComponent.onRefresh();
            mRefreshListener.onRefresh();
            mState = HeadRefreshComponent.RefreshState.STATE_REFRESH;
            mHeadRefreshComponent.setState(HeadRefreshComponent.RefreshState.STATE_REFRESH);
        }
    }

}
